<?xml version="1.0" encoding="UTF-8"?><d:tdl xmlns="http://www.w3.org/1999/xhtml" xmlns:b="http://www.backbase.com/2006/btl"  xmlns:d="http://www.backbase.com/2006/tdl" >

	<d:namespace name="http://www.backbase.com/2006/btl">

		<d:uses element="visualElement positionElement dimensionElement containerElement" src="../visualElement/visualElement.xml"/>
		<d:uses element="focusableElement" src="../focus/focus.xml"/>
		<d:uses element="dataTemplate" src="../dataTemplate/dataTemplate.xml"/>

		<d:element name="dataTreeBase" extends="b:dataTemplate b:positionElement b:dimensionElement b:containerElement b:focusableElement" abstract="true">
			

			

			

			

			

			<d:resource type="text/javascript"><![CDATA[
				if (!btl.dataTree)
					btl.dataTree = {};

				/**
				 * Closes a node and deselects and closes all its descendants
				 * @param oDataSource Reference to the datasource
				 * @param sItem The record ID of the item
				 * @param aSelectedHash The selectedIndexesHash object
				 */
				btl.dataTree.closeSelfAndDescendants = function(oDataSource, sItem, aSelectedHash) {
					var iCount = 0;
					if (btl.dataSource.getValue(oDataSource, sItem, 'open') == 'true') {
						var aItems = btl.dataSource.getLinked(oDataSource, sItem, 'item');
						for (var i = 0, len = aItems.length; i < len; i++) {
							if (aSelectedHash.hasOwnProperty(aItems[i])) {
								btl.dataSource.setValue(oDataSource, aItems[i], 'selected', 'false');
								delete aSelectedHash[aItems[i]];
								iCount++;
							}
							iCount += btl.dataTree.closeSelfAndDescendants(oDataSource, aItems[i], aSelectedHash);
						}
						btl.dataSource.setValue(oDataSource, sItem, 'open', 'false');
					}
					return iCount;
				}

				/**
				 * Builds instance node for a record and returns it
				 * @param oDocument The document to create the node for
				 * @param oDataSource Reference to the datasource
				 * @param sItem The record ID of the item
				 */
				btl.dataTree.buildInstanceItem = function(oDocument, oDataSource, sItem, aSelectedHash, sBaseUri) {
					var oItem = oDocument.createElement('item');

					oItem.setAttribute('id', sItem);
					var sName = btl.dataSource.getValue(oDataSource, sItem, 'name');
					if (sName.length !== null) {
						var oName = oDocument.createElement('name');
						oName.appendChild(oDocument.createTextNode(sName));
						oItem.appendChild(oName);
					}
					var sIcon = btl.dataSource.getValue(oDataSource, sItem, 'icon');
					if (sIcon !== null) {
						var oIcon = oDocument.createElement('icon');
						oIcon.appendChild(oDocument.createTextNode(bb.uri.resolveUri(sIcon, sBaseUri)));
						oItem.appendChild(oIcon);
					}
					var sIconOpen = btl.dataSource.getValue(oDataSource, sItem, 'iconOpen');
					if (sIconOpen !== null) {
						var oIconOpen = oDocument.createElement('iconOpen');
						oIconOpen.appendChild(oDocument.createTextNode(bb.uri.resolveUri(sIconOpen, sBaseUri)));
						oItem.appendChild(oIconOpen);
					}
					var sIconEmpty = btl.dataSource.getValue(oDataSource, sItem, 'iconEmpty');
					if (sIconEmpty !== null) {
						var oIconEmpty = oDocument.createElement('iconEmpty');
						oIconEmpty.appendChild(oDocument.createTextNode(bb.uri.resolveUri(sIconEmpty, sBaseUri)));
						oItem.appendChild(oIconEmpty);
					}
					var sBranch = btl.dataSource.getValue(oDataSource, sItem, 'branch');
					if (sBranch) {
						oItem.setAttribute('branch', sBranch);
					}
					var sEmpty = btl.dataSource.getValue(oDataSource, sItem, 'empty');
					if (sEmpty) {
						oItem.setAttribute('empty', sEmpty);
					}
					var sOpen = btl.dataSource.getValue(oDataSource, sItem, 'open');
					if (sOpen) {
						oItem.setAttribute('open', sOpen);
					}
					var sSelected = btl.dataSource.getValue(oDataSource, sItem, 'selected');
					if (sSelected) {
						oItem.setAttribute('selected', sSelected);
					}
					// update selectedIndexes property value
					if (sSelected == 'true') {
						aSelectedHash[sItem] = true;
					} else if (aSelectedHash.hasOwnProperty(sItem)) {
						delete aSelectedHash[sItem];
					}
					// recur if node is open
					if (sOpen == 'true') {
						var aItems = btl.dataSource.getLinked(oDataSource, sItem, 'item');
						for (var i=0, len = aItems.length; i < len; i++) {
							var oSubmenuItem = btl.dataTree.buildInstanceItem(oDocument, oDataSource, aItems[i], aSelectedHash, sBaseUri);
							oItem.appendChild(oSubmenuItem);
						}
					}
					return oItem;
				};
			]]></d:resource>

			<d:property name="instance">
				<d:getter type="text/javascript"><![CDATA[
					if (!this._._instance)
						this._._instance = bb.xml.parse('<?xml version="1.0" encoding="UTF-8"?><tree/>').documentElement;
					return this._._instance;
				]]></d:getter>
			</d:property>

			<d:property name="selectedIndexes">
				
				<d:getter type="text/javascript"><![CDATA[
					var selectedIndexesHash = this.getProperty('selectedIndexesHash');
					var selectedIndexes = [];
					for (var i in selectedIndexesHash) {
						if (selectedIndexesHash.hasOwnProperty(i))
							selectedIndexes[selectedIndexes.length] = i;
					}
					return selectedIndexes;
				]]></d:getter>
			</d:property>

			<d:property name="selectedIndexesHash">
				
				<d:getter type="text/javascript"><![CDATA[
					if (!this._._selectedIndexesHash)
						this._._selectedIndexesHash = {};
					return this._._selectedIndexesHash;
				]]></d:getter>
			</d:property>

			<d:method name="buildInstanceItem">
				<d:argument name="document"/>
				<d:argument name="dataSource"/>
				<d:argument name="recordID"/>
				<d:body type="text/javascript"><![CDATA[
					return btl.dataTree.buildInstanceItem(document, dataSource, recordID,
							this.getProperty('selectedIndexesHash'), this.getProperty('baseURI'));
				]]></d:body>
			</d:method>

			<d:attribute name="selectType" default="singleRequired">
				
			</d:attribute>

			<d:attribute name="direction" default="ltr">
				
				<d:mapper type="text/javascript"><![CDATA[
					if (value == 'rtl')
						bb.html.addClass( this.viewNode, 'btl-dataTree-rtl');
					else
						bb.html.removeClass( this.viewNode, 'btl-dataTree-rtl');
				]]></d:mapper>
			</d:attribute>

			<!-- Set either focusedNode or focusedId, they update eachother -->
			<d:property name="focusedNode">
				
				<d:setter type="text/javascript"><![CDATA[
					this.setFocusedNode(this._._focusedNode, value);
					this._._focusedNode = value;
					this._._focusedId = value ? this.getIdFromView(value) : null;
				]]></d:setter>
			</d:property>

			<!-- Set either focusedNode or focusedId, they update eachother -->
			<d:property name="focusedId">
				
				<d:getter type="text/javascript"><![CDATA[
					if (!this._._focusedId) {
						var selectedIndexes = this.getProperty('selectedIndexes');
						if (selectedIndexes.length) {
							this._._focusedId = selectedIndexes[0];
						} else {
							var indexes = this.getProperty('indexes');
							if (indexes.length) {
								this._._focusedId = indexes[0];
							} else {
								this._._focusedId = null;
							}
						}
					}
					return this._._focusedId;
				]]></d:getter>
				<d:setter type="text/javascript"><![CDATA[
					this._._focusedId = value;
					var viewNode = this.getViewNode(value),
						focusedNode = viewNode ? viewNode.firstChild : null;
					this.setFocusedNode(this._._focusedNode, focusedNode);
					this._._focusedNode = focusedNode;
				]]></d:setter>
			</d:property>

			<d:method name="clearSelection">
				
				<d:body type="text/javascript"><![CDATA[
					var oDataSource = this.getProperty('dataSource'),
						aSelected = this.getProperty('selectedIndexes'),
						aSelectedHash = this.getProperty('selectedIndexesHash'),
						start = this.getAttribute('selectType') == 'singleRequired' ? 1 : 0;
					for (var i = start, len = aSelected.length; i < len; i++) {
						btl.dataSource.setValue(oDataSource, aSelected[i], 'selected', 'false');
						delete aSelectedHash[aSelected[i]];
					}
				]]></d:body>
			</d:method>

			<d:method name="getOpen">
				
				<d:argument name="identifier">
					
				</d:argument>
				<d:body type="text/javascript"><![CDATA[
					var oDataSource = this.getProperty('dataSource');
					return btl.dataSource.getValue(oDataSource, identifier, 'open') == 'true';
				]]></d:body>
			</d:method>

			<d:method name="setOpen">
				
				<d:argument name="identifier">
					
				</d:argument>
				<d:argument name="value">
					
				</d:argument>
				<d:body type="text/javascript"><![CDATA[
					var oEvent = bb.document.createEvent('CustomEvent');
					oEvent.initCustomEventNS(null, value ? 'treeOpen' : 'treeClose', true, false, identifier);
					if (!this.dispatchEvent(oEvent))
						return;

					var oDataSource = this.getProperty('dataSource');

					// if opened
					if (value) {
						btl.dataSource.setValue(oDataSource, identifier, 'open', 'true');
					}
					// if closed
					else {
						// deselect all selected children
						var sSelectType = this.getAttribute('selectType'),
							aSelectedHash = this.getProperty('selectedIndexesHash'),
							iCount = btl.dataTree.closeSelfAndDescendants(oDataSource, identifier, aSelectedHash);

						// shift selection if singleRequired and no nodes selected anymore
						if (sSelectType == 'singleRequired' &&
								this.getProperty('selectedIndexes').length == iCount) {
							btl.dataSource.setValue(oDataSource, identifier, 'selected', 'true');
							aSelectedHash[identifier] = true;
						}
					}
				]]></d:body>
			</d:method>

			<d:method name="getSelected">
				
				<d:argument name="identifier">
					
				</d:argument>
				<d:body type="text/javascript"><![CDATA[
					var oDataSource = this.getProperty('dataSource');
					return btl.dataSource.getValue(oDataSource, identifier, 'selected') == 'true';
				]]></d:body>
			</d:method>

			<d:method name="setSelected">
				
				<d:argument name="identifier">
					
				</d:argument>
				<d:argument name="value">
					
				</d:argument>
				<d:body type="text/javascript"><![CDATA[
					var oDataSource = this.getProperty('dataSource');
					var sSelectType = this.getAttribute('selectType');
					var oEvent = bb.document.createEvent('CustomEvent');
					var aSelectedHash = this.getProperty('selectedIndexesHash');

					// if selected
					if (value) {
						oEvent.initCustomEventNS(null, 'treeSelect', true, false, identifier);
						if (!this.dispatchEvent(oEvent))
							return;

						if (sSelectType != 'multiple') {
							// make sure only one item is selected
							var aSelected = this.getProperty('selectedIndexes');
							for (var i = 0, iMax = aSelected.length ; i < iMax ; i++) {
								if (aSelected[i] != identifier) {
									btl.dataSource.setValue(oDataSource, aSelected[i], 'selected', 'false');
									delete aSelectedHash[aSelected[i]];
								}
							}
						}
						btl.dataSource.setValue(oDataSource, identifier, 'selected', 'true');
						aSelectedHash[identifier] = true;
					}
					// if deselected
					else {
						// depending on tree selection type
						if (sSelectType != 'singleRequired' ||
								(sSelectType == 'singleRequired' && this.getProperty('selectedIndexes').length == 1)) {
							oEvent.initCustomEventNS(null, 'treeDeselect', true, false, identifier);
							if (!this.dispatchEvent(oEvent))
								return;
							btl.dataSource.setValue(oDataSource, identifier, 'selected', 'false');
							delete aSelectedHash[identifier];
						}
					}
				]]></d:body>
			</d:method>

			<d:method name="getEmpty">
				
				<d:argument name="identifier">
					
				</d:argument>
				<d:body type="text/javascript"><![CDATA[
					var oDataSource = this.getProperty('dataSource');
					return btl.dataSource.getValue(oDataSource, identifier, 'empty') == 'true';
				]]></d:body>
			</d:method>

			<d:method name="setEmpty">
				
				<d:argument name="identifier">
					
				</d:argument>
				<d:argument name="value">
					
				</d:argument>
				<d:body type="text/javascript"><![CDATA[
					var oDataSource = this.getProperty('dataSource');
					btl.dataSource.setValue(oDataSource, identifier, 'empty', value ? 'true' : 'false');
				]]></d:body>
			</d:method>

			<d:method name="getBranch">
				
				<d:argument name="identifier">
					
				</d:argument>
				<d:body type="text/javascript"><![CDATA[
					var oDataSource = this.getProperty('dataSource');
					return btl.dataSource.getValue(oDataSource, identifier, 'branch') == 'true';
				]]></d:body>
			</d:method>

			<d:method name="getDescendants">
				
				<d:argument name="identifier">
					
				</d:argument>
				<d:body type="text/javascript"><![CDATA[
					var oDataSource = this.getProperty('dataSource');
					var aResult = [];
					btl.dataTree.getSelfAndDescendants(oDataSource, identifier, aResult);
					aResult.shift();
					return aResult;
				]]></d:body>
			</d:method>

			<d:handler event="DOMNodeInsertedIntoDocument" type="text/javascript"><![CDATA[
				bb.html.disableUserSelect(this.viewNode);
				if (bb.browser.ie) {
					var oC = this;
					var func = function() {oC.__focused = false;}
					this.addEventListener('mouseup', func, false);
					this.addEventListener('mouseleave', func, false);
				}
			]]></d:handler>

			<d:handler event="mousedown" match="[btl_data-id]" type="text/javascript"><![CDATA[
				var sId = this.getIdFromView(event.currentView);
				if (sId == this.getIdFromView(event.viewTarget)) {
					// if user clicks on leaf node
					this.setProperty('focusedNode', event.currentView.firstChild);

					// Position focusElement at mouse position to avoid jump when top of tree is scrolled out of view
					var iScrollX = btl.getScrollLeft(this.viewNode);
					var iScrollY = btl.getScrollTop(this.viewNode);
					var aCoordinates = bb.html.getBoxObject(this.viewNode);
					var iNewX = event.clientX + iScrollX - aCoordinates[0] + 1;
					var iNewY= event.clientY + iScrollY - aCoordinates[1] + 1;
					var oFocusElement = this.getProperty('focusElement');
					oFocusElement.style.left = iNewX + 'px';
		 			oFocusElement.style.top = iNewY + 'px';
		 			// focus
					this.__focused = true;
					this.focus();
					event.preventDefault();
				}
			]]></d:handler>

			<d:handler event="mousedown" match=".btl-dataTree-toggleOpenClose" type="text/javascript"><![CDATA[
				// if user clicked on open/close toggle button of branch
				var oNode = event.viewTarget;
				while (oNode != event.currentView &&
						!bb.html.hasClass(oNode, 'btl-dataTree-selectAndToggle'))
					oNode = oNode.parentNode;
				if (oNode == event.currentView) {
					var sId = this.getIdFromView(event.currentView);
					if (!this.getEmpty(sId)) {
						// if user clicks on non-empty branch node
						this.setOpen(sId, !this.getOpen(sId));
						btl.dataSource.actionRequest(this, 'update');
					}
				}
			]]></d:handler>

			<d:handler event="mousedown" match=".btl-dataTree-selectAndToggle" type="text/javascript"><![CDATA[
				var sId = this.getIdFromView(event.viewTarget);
				if (sId !== null) {
					if (event.detail == 2) {
						// double-click
						var sId = this.getIdFromView(event.viewTarget);
						if (!this.getEmpty(sId)) {
							this.setOpen(sId, !this.getOpen(sId));
							btl.dataSource.actionRequest(this, 'update');
						}
					} else {
						// single click
						var oDataSource = this.getProperty('dataSource');
						var sSelectType = this.getAttribute('selectType');

						// when ctrl is pressed
						var bCtrl = !bb.browser.opera && navigator.platform.indexOf('Mac') != -1 ? event.metaKey : event.ctrlKey;
						if (( sSelectType == 'multiple' || sSelectType == 'single' ) && bCtrl) {
							this.setSelected(sId, !this.getSelected(sId));
						} else {
							if (sSelectType == 'multiple')
								this.clearSelection();
							this.setSelected(sId, true);
						}

						btl.dataSource.actionRequest(this, 'update');

						// included to prevent the visible focus border disappearing in Opera 9
						if (bb.browser.opera)
							event.preventDefault();
					}
				}
			]]></d:handler>

			<d:handler event="keydown" type="text/javascript"><![CDATA[
				if (event.target == this) {
					var sFocusedId = this.getProperty('focusedId'),
						oFocusedNode = this.getInstanceNode(sFocusedId),
						oTargetNode,
						keyIdent = event.keyIdentifier,
						bCtrl = navigator.platform.indexOf('Mac') != -1 ? event.metaKey || event.ctrlKey : event.ctrlKey;

					if (this.getAttribute('direction') == 'rtl')
						keyIdent = keyIdent == "Right" ? "Left" : keyIdent == "Left" ? "Right" : keyIdent;

					switch (keyIdent)
					{
						case "Up":
							event.preventDefault();
							if ((oTargetNode = oFocusedNode.previousSibling) && oTargetNode.nodeName == 'item') {
								while (oTargetNode.getAttribute('open') == 'true' && oTargetNode.lastChild)
									oTargetNode = oTargetNode.lastChild;
							} else {
								oTargetNode = oFocusedNode.parentNode;
							}
							break;
						case "Down":
							event.preventDefault();
							if (oFocusedNode.getAttribute('open') == 'true') {
								oTargetNode = oFocusedNode.firstChild;
								while (oTargetNode && oTargetNode.nodeName != 'item')
									oTargetNode = oTargetNode.nextSibling;
							} else {
								oTargetNode = oFocusedNode;
								while (oTargetNode.nodeType == 1 && !oTargetNode.nextSibling)
									oTargetNode = oTargetNode.parentNode;
								if (oTargetNode.nodeType == 1)
									oTargetNode = oTargetNode.nextSibling;
								else
									break;
							}
							break;
						case "Left":
							event.preventDefault();	// prevent navigating back/forward
							if (this.getOpen(sFocusedId)) {
								this.setOpen(sFocusedId, false);
								btl.dataSource.actionRequest(this, 'update');
							} else {
								oTargetNode = oFocusedNode.parentNode;
							}
							break;
						case "Right":
							event.preventDefault();	// prevent navigating back/forward
							if (this.getOpen(sFocusedId)) {
								oTargetNode = oFocusedNode.firstChild;
								while (oTargetNode && oTargetNode.nodeName != 'item')
									oTargetNode = oTargetNode.nextSibling;
							} else if (this.getBranch(sFocusedId) && !this.getEmpty(sFocusedId)) {
								this.setOpen(sFocusedId, true);
								btl.dataSource.actionRequest(this, 'update');
							}
							break;
						case "Enter":
						case "U+0020":
							this.setSelected(sFocusedId, true);
							btl.dataSource.actionRequest(this, 'update');
							break;
					}
					if (oTargetNode && oTargetNode.nodeType == 1 && oTargetNode.nodeName == 'item') {
						var sNewFocusedId = oTargetNode.getAttribute('id');
						this.setProperty('focusedId', sNewFocusedId);
						if (!bCtrl) {
							this.clearSelection();
							this.setSelected(sNewFocusedId, true);
							btl.dataSource.actionRequest(this, 'update');
						}
					}
				}
			]]></d:handler>

		</d:element>
	</d:namespace>
</d:tdl>